#include "objc-private.h"
#include <objc/message.h>
#include <map>
#include "DenseMapExtras.h"

// MARK: - 对象缓存策略
enum {
    // assgin 修饰: 基本类型/ 解决循环引用的对象
    OBJC_ASSOCIATION_SETTER_ASSIGN      = 0,
    // retain 修饰: 需要retain 操作的 对象
    OBJC_ASSOCIATION_SETTER_RETAIN      = 1,
    // copy 修饰: 需要copy 操作的 对象, block, string....
    OBJC_ASSOCIATION_SETTER_COPY        = 3,
    
    OBJC_ASSOCIATION_GETTER_READ        = (0 << 8), // readonly的属性
    OBJC_ASSOCIATION_GETTER_RETAIN      = (1 << 8), // 获取值的时候需要retain
    OBJC_ASSOCIATION_GETTER_AUTORELEASE = (2 << 8), // 获取值的时候需要autorelease
    // 系统对象
    OBJC_ASSOCIATION_SYSTEM_OBJECT      = _OBJC_ASSOCIATION_SYSTEM_OBJECT,
};

// 自旋锁
spinlock_t AssociationsManagerLock;

// MARK: - NOTE
/*
 * 1. AssociationsManager: 是全局单例对象, 维护 AssociationsHashMap 集合.
 *
 * 2. AssociationsHashMap: 是一个无序的哈希表, 维护对象内部所有管理记录
 Key: 源对象,
 value: ObjectAssociationMap的映射.

 * 3. ObjectAssociationMap: 也是一个无序的哈希表, 负责维护源对象内部的关联记录.
 Key: 传过来的关联Key,
 value: ObjcAssociation 对象的映射.
 
 * 4. ObjcAssociation: 关联对象, 是一个 C++ 的类, 表示一个具体的关联结构,
 主要包括两个实例变量, _policy 表示关联策略, _value 表示关联对象.
 */

// 命名空间
using namespace objc;
namespace objc {

// MARK: - 关联对象类
class ObjcAssociation {
    uintptr_t _policy;
    id _value;
public:
    ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
    ObjcAssociation() : _policy(0), _value(nil) {}
    ObjcAssociation(const ObjcAssociation &other) = default;
    ObjcAssociation &operator=(const ObjcAssociation &other) = default;
    ObjcAssociation(ObjcAssociation &&other) : ObjcAssociation() {
        swap(other);
    }
    
    // 交换key & value值
    inline void swap(ObjcAssociation &other) {
        std::swap(_policy, other._policy);
        std::swap(_value, other._value);
    }
    
    inline uintptr_t policy() const { return _policy; }
    inline id value() const { return _value; }
    
    // 根据策略对 对象做retain操作
    inline void acquireValue() {
        if (_value) {
            switch (_policy & 0xFF) {
                case OBJC_ASSOCIATION_SETTER_RETAIN:
                    _value = objc_retain(_value);
                    break;
                case OBJC_ASSOCIATION_SETTER_COPY:
                    _value = ((id(*)(id, SEL))objc_msgSend)(_value, @selector(copy));
                    break;
            }
        }
    }
    
    // 根据策略对 对象做release操作
    inline void releaseHeldValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_SETTER_RETAIN)) {
            objc_release(_value);
        }
    }
    
    // 取值. 根据策略对 对象做retain操作
    inline void retainReturnedValue() {
        if (_value && (_policy & OBJC_ASSOCIATION_GETTER_RETAIN)) {
            objc_retain(_value);
        }
    }
    
    // 根据策略对 对象做autorelease操作
    inline id autoreleaseReturnedValue() {
        if (slowpath(_value && (_policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE))) {
            return objc_autorelease(_value);
        }
        return _value;
    }
};

// llvm-DenseMap(无序的哈希表).h
/**
 * 定义一个hashMap类 ObjectAssociationMap
 * Key: string
 * Value: ObjcAssociation类型
 */
typedef DenseMap<const void *, ObjcAssociation> ObjectAssociationMap;

/**
 * 定义一个hashMap类 AssociationsHashMap
 * Key: objc_object(源对象)
 * Value: ObjectAssociationMap类型
 */
typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;

/**
 * 定义一个 关联map管理类, 单例对象
 */
class AssociationsManager {
    using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
    static Storage _mapStorage;
    
public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }
    
    /// 获取所有关联map
    AssociationsHashMap &get() {
        return _mapStorage.get();
    }
    
    static void init() {
        _mapStorage.init();
    }
};

AssociationsManager::Storage AssociationsManager::_mapStorage;

}

// MARK: - 初始化入口
/**
 * 什么时候初始化的?
 * dyld`start .......
 * objc-os.mm          -> _objc_init()
 * objc-runtime-new.mm -> map_images()
 * objc-os.mm          -> map_images_nolock()
 * NSObject.mm         -> arr_init()
 * _objc_associations_init()
 */
void
_objc_associations_init()
{
    // 初始化单例对象
    AssociationsManager::init();
}

/** 
 * 从源对象取出关联对象值
 * Parameter @object: 关联的源对象
 * Parameter @key:    关联键
 */
id
_object_get_associative_reference(id object, const void *key)
{
    // 声明一个空的关联对象
    ObjcAssociation association{};
    
    {
        // 全局关联map管理者
        AssociationsManager manager;
        // 得到所有的关联Hash表数据
        AssociationsHashMap &associations(manager.get());
        // 根据 源对象 遍历找到源对象内部的关联map
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        // 判断迭代器不在最后表示不为空
        if (i != associations.end()) {
            // i->first  得到Key
            // i->second 得到Value ObjectAssociationMap
            ObjectAssociationMap &refs = i->second;
            // 根据 key 遍历ObjectAssociationMap 找到对应值的迭代器
            ObjectAssociationMap::iterator j = refs.find(key);
            // 判断迭代器不在最后表示不为空
            if (j != refs.end()) {
                // 关联对象
                association = j->second;
                // 操作引用计数
                association.retainReturnedValue();
            }
        }
    }
    
    // 加入自动释放池
    return association.autoreleaseReturnedValue();
}

/* 源对象关联某个对象值底层函数
 * Parameter @object: 关联的源对象
 * Parameter @key:    关联键
 * Parameter @value:  关联值
 * Parameter @policy: 策略
 */
void
_object_set_associative_reference(id object, const void *key, id value, uintptr_t policy)
{
    // 源对象 和 关联的值 都是空 return
    if (!object && !value) return;
    
    // 检查当前对象是否不允许关联属性，某些类不允许其实例有关联属性
    if (object->getIsa()->forbidsAssociatedObjects())
        _objc_fatal("objc_setAssociatedObject called on instance (%p) of class %s which does not allow associated objects", object, object_getClassName(object));
    
    // 将源对象 包装成 DisguisedPtr<objc_object>
    DisguisedPtr<objc_object> disguised{(objc_object *)object};
    
    // 初始化一个 ObjcAssociation 类型的 值对象(不是指针(引用对象))
    ObjcAssociation association{policy, value};
    
    // retain the new value (if any) outside the lock.
    // 根据策略对 对象做retain操作
    association.acquireValue();
    
    bool isFirstAssociation = false;
    {
        // 声明一个 AssociationsManager类型对象, 但是AssociationsManager内部是个单例
        AssociationsManager manager;
        // 得到所有的关联Hash表数据
        AssociationsHashMap &associations(manager.get());
        
        // 关联值空判断
        if (value) { // 不为空, 关联操作
            // C++17 引入了 std::try_emplace,
            // try_emplace: llvm-DenseMap.h 有实现
            // 该方法会检测指定的key是否存在, 存在: 什么也不做; 不存在: 插入相应的value
            // 知道try_emplace含义后, 可以理解是对 源对象 的 ObjectAssociationMap 初始化操作
            auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{});
            // 如果是第一次, second 为true
            if (refs_result.second) {
                /* it's the first association we make */
                isFirstAssociation = true;
            }
            
            /* establish or replace the association */
            // refs_result->first  得到Key: bool
            // refs_result->second 得到Value: iterator
            // iterator.ObjectAssociationMap
            auto &refs = refs_result.first->second;
            
            // C++17引入了std::try_emplace，
            // 在参数列表中，把key和value分开，该方法会检测指定的key是否存在，
            // 如果存在，什么也不做，不存在，则插入相应的value
            // 可以理解是对 ObjectAssociationMap 的 key值 初始化操作
            auto result = refs.try_emplace(key, std::move(association));
            // 如果 key: 对应的value无值 second = true
            // 如果 key: 对应的value有值 second = false
            // 无值是直接移动进key对应值空间内,
            // 有值就不会动 try_emplace 函数特性, 所以需要swap 交换一下
            if (!result.second) {
                association.swap(result.first->second);
            }
        }
        else { // 为空, 清空操作
            // 在 map 容器中查找键为 key 的键值对,
            // 如果成功找到, 则返回指向该键值对的 双向迭代器;
            // 反之, 则返回和 end() 方法一样的迭代器.
            // 另外, 如果 map 容器用 const 限定, 则该方法返回的是 const 类型的双向迭代器
            auto refs_it = associations.find(disguised); // ObjectAssociationMap 类型
            // 判断迭代器不在最后表示不为空
            if (refs_it != associations.end()) {
                // refs->first  得到Key
                // refs->second 得到Value: ObjectAssociationMap 类型
                auto &refs = refs_it->second;
                // 在次查找 key 对应的迭代器
                auto it = refs.find(key); // ObjcAssociation 类型
                // 判断迭代器不在最后表示不为空
                if (it != refs.end()) {
                    // it->first  得到Key
                    // it->second 得到Value: ObjcAssociation 类型
                    // swap: 交换 2 个 map 容器中存储的键值对，这意味着，操作的 2 个键值对的类型必须相同
                    // 因为association这个 是个空值, 所以变相清空了内部值
                    association.swap(it->second);
                    // 删除 map 容器指定位置、指定键（key）值或者指定区域内的键值对
                    refs.erase(it);
                    // 返回当前 map 容器中存有键值对的个数
                    // 如果当前ObjectAssociationMap 为空
                    if (refs.size() == 0) {
                        // 删除 map 容器指定位置、指定键（key）值或者指定区域内的键值对
                        // 删除 ObjectAssociationMap
                        associations.erase(refs_it);
                    }
                }
            }
        }
    }
    
    // Call setHasAssociatedObjects outside the lock, since this
    // will call the object's _noteAssociatedObjects method if it
    // has one, and this may trigger +initialize which might do
    // arbitrary stuff, including setting more associated objects.
    if (isFirstAssociation)
        // 修改object的isa指针, 标记为有关联对象, 在object的dealloc中有用
        object->setHasAssociatedObjects();
    
    // release the old value (outside of the lock).
    // 对旧值的释放
    association.releaseHeldValue();
}

/* 移除 源对象 的 关联
 * Parameter @object: 关联的源对象
 * Parameter @deallocating: 是否回收
 ** objc_destructInstance        = true
 ** objc_removeAssociatedObjects = false
 */
void
_object_remove_assocations(id object, bool deallocating)
{
    // 初始化一个关联map ObjectAssociationMap
    ObjectAssociationMap refs{};
    
    {
        // 得到所有的关联Hash表数据
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.get());
        // 找到 源对象的 关联map AssociationsHashMap迭代器
        AssociationsHashMap::iterator i = associations.find((objc_object *)object);
        // 判断迭代器不在最后表示不为空
        if (i != associations.end()) {
            // 用空数据交换 当前源对象关联map ObjectAssociationMap
            refs.swap(i->second);
             
            // 如果不回收, 保留关联 OBJC_ASSOCIATION_SYSTEM_OBJECT
            // 保留关联对象是否重新插入到关联map ObjectAssociationMap 内
            bool didReInsert = false;
            if (!deallocating) { // 不回收
                // 因为空的 refs 被swap 交换后,
                // 保存的是源对象 之前内的 ObjectAssociationMap,
                // 现在源对象内部是一个空的 ObjectAssociationMap
                // 遍历关联map下面所有的 节点(ObjectAssociation)
                for (auto &ref: refs) {
                    // ObjectAssociationMap.ObjectAssociation.policy
                    // 判断 策略policy 是否包含 OBJC_ASSOCIATION_SYSTEM_OBJECT
                    if (ref.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) { // 保留关联
                        // 插入到空 AssociationsHashMap内保留, 因为上面 i 已经被替换成空的map
                        i->second.insert(ref);
                        // 标记 保留后面不删除 AssociationsHashMap
                        didReInsert = true;
                    }
                }
            }
            // 不保留 直接删除
            if (!didReInsert)
                associations.erase(i);
        }
    }
    
    // objc-private(局部缓存数组).h
    // 存储待释放的关联, 过滤保留值
    SmallVector<ObjcAssociation *, 4> laterRefs;
    
    // release everything (outside of the lock).
    // 因为空的 refs 被swap 交换后,
    // 保存的是源对象 之前内的 AssociationsHashMap,
    // 现在 源对象 内部AssociationsHashMap存放
    // 遍历关联map下面所有的 节点(ObjectAssociationMap)
    for (auto &i: refs) {
        // ObjectAssociationMap.ObjectAssociation.policy
        // 判断 策略policy 是否包含 OBJC_ASSOCIATION_SYSTEM_OBJECT
        if (i.second.policy() & OBJC_ASSOCIATION_SYSTEM_OBJECT) {
            // If we are not deallocating, then RELEASE_LATER associations don't get released.
            
            if (deallocating) // 如果回收, 添加到待回收的数组中
                laterRefs.append(&i.second);
        } else { // 非 OBJC_ASSOCIATION_SYSTEM_OBJECT 内存策略
            // 释放 ObjectAssociation
            i.second.releaseHeldValue();
        }
    }
    // 对 deallocating 回收 的OBJC_ASSOCIATION_SYSTEM_OBJECT 值 release
    for (auto *later: laterRefs) {
        later->releaseHeldValue();
    }
}
